package p01;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

import java.lang.reflect.Field;
import java.util.Map;

/**
 * BeanFactory 是最顶层的容器接口，她定义了容器的基本功能，是spring实现控制反转和依赖注入的核心机制。确实是这样的！
 * 🧱 一、基本概念
 * BeanFactory 是 Spring 容器的 基础接口，提供了获取 Bean 的能力。
 * 它负责 管理 Bean 的生命周期 和 依赖关系。
 * BeanFactory 只是一个接口，实际使用的是其子类如 DefaultListableBeanFactory 等
 *
 * @author Chen weishuai
 * @since 2025/7/14
 */
@Slf4j
@SpringBootApplication
public class BeanFactoryDemo {

    class Dog {
        public void say() {
            System.out.println("汪汪汪");
        }
    }

    @Bean
    public Dog puppy() {
        return new Dog();
    }

    /**
     * BeanFactory是ApplicationContext的父接口，它才是spring的核心容器
     * 而ApplicationContext的实现类都组合了它的功能,BeanFactory是ApplicationContext实现类的成员属性
     */
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

        /**
         * ConfigurableApplicationContext继承自ApplicationContext接口，ApplicationContext接口继承自BeanFactory
         * context.getBean()实际调用的方法来源：DefaultListableBeanFactory （实际创建Bean）
         */
        ConfigurableApplicationContext context = SpringApplication.run(BeanFactoryDemo.class, args);
        Object puppy = context.getBean("puppy");
        ((Dog) puppy).say();
        //  实际上spring依赖注入，控制反转，生命周期都由其实现类DefaultListableBeanFactory实现
        Class<? extends ConfigurableApplicationContext> aClass = context.getClass();
        log.info("容器的实际类型：{}", aClass);
        /**
         *  DefaultListableBeanFactory的父类DefaultSingletonBeanRegistry维护了一个map，维护了所有单例bean
         *  该map由private final修饰的，无法被子类继承，可以通过反射得到该map
         */
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
        singletonObjects.setAccessible(true);
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        if (map != null) {
            map.forEach((k, v) -> log.info(k + "==>" + v));
        }
        SpringApplication.exit(context, () -> 0);

    }
}
